package sk.naive.talker;

import sk.naive.talker.message.DefaultMessageFactory;
import sk.naive.talker.util.Utils;
import sk.naive.talker.plugin.*;

import javax.mail.*;
import javax.mail.internet.*;
import java.rmi.RemoteException;
import java.util.*;

/**
 * MailHelper.
 *
 * @author <a href="mailto:virgo@naive.deepblue.sk">Richard "Virgo" Richter</a>
 * @version $Revision: 1.8 $ $Date: 2007-05-07 10:34:20 $
 */
public abstract class MailHelper implements TalkerPlugin {
	public static final String TPROP_MAIL_HELPER = "mailHelper";
	public static final String TPROP_MAIL_STORE_HOST = "mailStoreHost";
	public static final String TPROP_MAIL_TRANSPORT_HOST = "mailTransportHost";

	private static final String PLUGIN_NAME = "mail";

	protected Talker talker;
	private Session session;
	private Map<Integer, Folder> loginIdInbox;
	private String storeSuffix;
	private String transportSuffix;
	private TimerTask timerTask;
	private Configuration configuration;

	public MailHelper() {
		loginIdInbox = new HashMap<Integer, Folder>();
	}

	public String name() {
		return PLUGIN_NAME;
	}

	public void init(Talker talker) {
		this.talker = talker;
		talker.getProperties().put(TPROP_MAIL_HELPER, this);
		storeSuffix = "@" + getMailStoreHost();
		transportSuffix = "@" + getTransportHost();
		timerTask = new TimerTask("mail checker") {
			public void execute() {
				try {
					periodicCheck();
				} catch (MessagingException e) {
					Utils.unexpectedExceptionWarning(e);
				}
			}
		};
		talker.timerService().schedule(timerTask, 2000, 20000);
	}

	public void setConfig(Configuration conf) {
		configuration = conf;
	}

	public Configuration getConfiguration() {
		return configuration;
	}

	public void destroy() {
		talker.getProperties().remove(TPROP_MAIL_HELPER);
		timerTask.cancel();
		// TODO dorobit podla uvazenia
	}

	public void userOut(User user) throws PluginException {
		try {
			getInbox(user).close(false);
		} catch (MessagingException e) {
			throw new PluginException(e);
		}
	}

	public void userIn(User user) throws PluginException {
		try {
			Folder f = getInbox(user);
			int unread = f.getUnreadMessageCount();
			f.close(false);
			if (unread > 0) {
				user.getProperties().put(DefaultMessageFactory.CTXKEY_VAL, String.valueOf(unread));
				user.send(talker.messageFactory().getString("server.unreadMail", user.getProperties()));
			}
		} catch (MessagingException e) {
			throw new PluginException(e);
		} catch (RemoteException e) {
			throw new PluginException(e);
		}
	}

	public Folder getInbox(final User user) throws MessagingException {
		Folder folder = loginIdInbox.get(user.getId());
		if (folder == null || !folder.getStore().isConnected()) {
			folder = getRealInbox(user);
			loginIdInbox.put(user.getId(), folder);
		}
		if (!folder.isOpen()) {
			folder.open(Folder.READ_WRITE);
		}
		return folder;
	}

	public Session getSession() {
		if (session == null) {
			// TODO: throws NPE if unconfifugred... but this should prevent plugin loading in the first place
			System.setProperty("mail.smtp.host", getTransportHost());
			session = Session.getInstance(System.getProperties());
		}
		return session;
	}

	public String getTransportHost() {
		return (String) talker.getProperties().get(TPROP_MAIL_TRANSPORT_HOST);
	}

	protected abstract Folder getRealInbox(User user) throws MessagingException;

	public void periodicCheck() throws MessagingException {
		for (User user : talker.userFinder().onlineUsers()) {
			Folder f = getRealInbox(user);
			int count = f.getNewMessageCount();
			if (count > 0) {
				newMailNotification(user, count);
			}
			f.close(false);
		}
	}

	private void newMailNotification(User user, int count) {
		try {
			user.getProperties().put(DefaultMessageFactory.CTXKEY_VAL, String.valueOf(count));
			user.send(talker.messageFactory().getString("server.newMailNotification", user.getProperties()));
		} catch (RemoteException e) {
			Utils.unexpectedExceptionWarning(e);
		}
	}

	public void sendMail(String fromLogin, InternetAddress[] toAddresses, String subject, String text) throws MessagingException {
		MimeMessage msg = new MimeMessage(getSession());
		msg.setFrom(new InternetAddress(createTransportAddress(fromLogin)));
		msg.setRecipients(Message.RecipientType.TO, toAddresses);
		msg.setSubject(subject);
		msg.setHeader("X-Mailer", "NaiveTalker");
		msg.setSentDate(new Date());
		msg.setText(text);
		Transport.send(msg);
	}

	public void sendMail(String fromLogin, InternetAddress[] toAddresses, String subject, Message message) throws MessagingException {
		message.setFrom(new InternetAddress(createTransportAddress(fromLogin)));
		message.setRecipients(Message.RecipientType.TO, toAddresses);
		message.setSubject(subject);
		message.setHeader("X-Mailer", "NaiveTalker");
		message.setSentDate(new Date());
		Transport.send(message);
	}

	/**
	 * Create address list - eliminate non-existing talker users.
	 *
	 * @param addressString string containing addresses
	 * @return array of addresses
	 */
	public InternetAddress[] createAddresses(String addressString) throws AddressException, RemoteException {
		InternetAddress[] parsedAddresses = InternetAddress.parse(addressString);
		Set<InternetAddress> addresses = new LinkedHashSet<InternetAddress>();
		for (InternetAddress address : parsedAddresses) {
			if (address.getAddress().indexOf('@') == -1) {
				User ntUser = findTarget(address.getAddress());
				if (ntUser != null) {
					address.setAddress(createTransportAddress(ntUser.getLogin()));
					addresses.add(address);
				}
			} else {
				addresses.add(address);
			}
		}
		return addresses.toArray(new InternetAddress[addresses.size()]);
	}

	public String addressString(Address[] addresses) {
		if (addresses == null || addresses.length < 1) {
			return "N/A";
		}
		if (addresses.length == 1) {
			return addresses[0].toString();
		}
		StringBuilder sb = new StringBuilder();
		for (Address address : addresses) {
			if (sb.length() > 0) {
				sb.append(", ");
			}
			sb.append(address.toString());
		}
		return sb.toString();
	}

	public String flagsString(Flags flags) {
		StringBuilder sb = new StringBuilder("--N");
		if (flags.contains(Flags.Flag.ANSWERED)) {
			sb.setCharAt(0, 'A');
		}
		if (flags.contains(Flags.Flag.DELETED)) {
			sb.setCharAt(1, 'D');
		}
		if (flags.contains(Flags.Flag.SEEN)) {
			sb.setCharAt(2, '-');
		}
		return sb.toString();
	}

	public String getMailStoreHost() {
		return (String) talker.getProperties().get(TPROP_MAIL_STORE_HOST);
	}

	public String getStoreSuffix() {
		return storeSuffix;
	}

	public String getTransportSuffix() {
		return transportSuffix;
	}

	public String createStoreAddress(String login) {
		return login + storeSuffix;
	}

	public String createTransportAddress(String login) {
		return login + transportSuffix;
	}

	public User findTarget(String targetLogin) throws RemoteException {
		return talker.userFinder().findUserByLogin(targetLogin);
	}
}
